package com.dc.commons.query;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.domain.Specification;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/*******************************************************************************<P>
 * 文件名: DynamicSpecifications.java <br>
 * 版本: 1.0<br>
 * 描述: 动态查询构建<P>
 *  "search_LK_userCode":"admin"<P>
	"search_LK_userCode":function(){return $("#userCode").val()}(),<P>
	"search_OR":function(){return {EQ_dataStatus:"VALID",EQ_userName:"admin1"}}(),<P>
	"search_OR":{EQ_dataStatus:"VALID",EQ_userName:"admin"},<P>
	"search_OR_EQ_dataStatus|LK_userName":["VALID","admin1"],<P>
	 "search_OR_LK_userCode|LK_userName":"admin",<P>
	"search_EXISTS_com.dc.modules.sys.user.domain.DcSysUserRole.sysRole":{<br>
		"EQ_id":"admin"<br>
	},<P>
	"search_EXISTS_com.dc.modules.sys.user.domain.DcSysUserRole.sysRole":{<br>
		"EQ_id":function(){return "admin"}()<br>
	}<P>
	"search_LK_userCode":"admin"<P>
 * <br>
 * 版权所有: <br>
 * <P>
 * 创建者: 王建栋 <br>
 * 创建日期: 2016年5月31日 下午7:28:04<br>
 * 修改者: <br>
 * 修改日期: <br>
 * 修改说明: <br>
 ******************************************************************************/
public class DynamicSpecifications {

	private static Logger logger=LoggerFactory.getLogger(DynamicSpecifications.class);
	
	static enum Group{
		
		AND{
			public Predicate expression(CriteriaBuilder builder,List<Predicate> predicates){
				return builder.and(predicates.toArray(new Predicate[predicates.size()]));
			 }
		},OR{
			public Predicate expression(CriteriaBuilder builder,List<Predicate> predicates){
				return builder.or(predicates.toArray(new Predicate[predicates.size()]));
			 }
		};
		
		public abstract Predicate expression(CriteriaBuilder builder,List<Predicate> predicates);
	}
	
	
	/**
	 * 构建And 
	 * @param dcQuery
	 * @return Specification
	 */
	public static <T> Specification<T> buildORDcQuery(final DcQuery dcQuery,Class<T> clazz){
		return buildDcQuery(dcQuery,Group.OR,clazz);
	}
	
	/**
	 * 构建And 
	 * @param dcQuery
	 * @return Specification
	 */
	public static <T> Specification<T> buildAndDcQuery(final DcQuery dcQuery,Class<T> clazz){
		return buildDcQuery(dcQuery,Group.AND,clazz);
	}
	
	/**
	 * 构建查询
	 * @param dcQuery
	 * @return Specification
	 */
	public static <T> Specification<T> buildDcQuery(final DcQuery dcQuery,final Group group,Class<T> clazz){
		return buildDcQuery(buildSearchFilter(dcQuery.getQueryMap()), group, clazz);
	}
	
	/**
	 * 构建查询
	 * @param searchFilterMap
	 * @param group
	 * @param clazz
	 * @return Specification
	 */
	public static <T> Specification<T> buildDcQuery(final List<SearchFilter> searchFilterMap,final Group group,Class<T> clazz){
		return new Specification<T>() {

			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
				List<Predicate> predicates=Lists.newArrayList();
				
				for(SearchFilter searchFilter:searchFilterMap){
					
					// OR的时候 没有 expression
					if(searchFilter.getQueryOperator()==QueryOperator.OR||searchFilter.getQueryOperator()==QueryOperator.EXISTS||searchFilter.getQueryOperator()==QueryOperator.NEXISTS){
						predicates.add(searchFilter.getQueryOperator().expression(root, query, builder, null, searchFilter));
					}else{
						Expression<?> expression=searchFilter.getQueryOperator().getExpresion(root,searchFilter.getSearchName());
						predicates.add(searchFilter.getQueryOperator().expression(root, query, builder, expression, searchFilter));
					}
				}
				if(predicates.isEmpty()){
					return builder.conjunction();
				}
				return group.expression(builder, predicates);
			}
			
		};
		
	}
	
	/**
	 * 整合为查询条件
	 * @param queryMap
	 * @return buildSearchFilter
	 */
	@SuppressWarnings("unchecked")
	public static List<SearchFilter> buildSearchFilter(Map<String,Object> queryMap){
		List<SearchFilter> filters=Lists.newArrayList();
		for(Entry<String,Object> entry:queryMap.entrySet()){
			// 过滤掉空值
			String key = entry.getKey();
			Object value = entry.getValue();
			if(value==null)continue;
			
			if (value instanceof String&&StringUtils.isBlank((String) value)) {
				continue;
			}
			
			String[] names = StringUtils.split(key, "_",2);
			QueryOperator operator=QueryOperator.valueOf(names[0]);
			String searchName=null;
			if(operator==QueryOperator.OR){
				
				//"search_OR":{EQ_dataStatus:"VALID",EQ_userName:"admin"}
				if(value instanceof Map){
					value=buildSearchFilter((Map<String,Object>)value);
					
				//"search_OR_EQ_dataStatus|LK_userName":["VALID","admin1"],
				} else if(value.getClass().isArray()){
					String[] subNames=StringUtils.split(names[1],"|");
					Object[] values=(Object[]) value;
					if(values.length!=1&&subNames.length!=values.length){
						logger.error("查询对象与查询值不一致{},{}", subNames,values);
						throw new RuntimeException("查询对象与查询值不一致");
					}
					
					// 将或查询 的数据 组装成一个Map 
					Map<String,Object> map=Maps.newHashMap();
					for(int i=0;i<subNames.length;i++){
						String subName=subNames[i];
						map.put(subName, values.length==1?value:values[i]);
					}
					value=buildSearchFilter(map);
				
					//"search_OR_LK_userCoe|LK_userName":"admin",
				}else{
					String[] subNames=StringUtils.split(names[1],"|");
					
					// 将或查询 的数据 组装成一个Map 
					Map<String,Object> map=Maps.newHashMap();
					for(int i=0;i<subNames.length;i++){
						String subName=subNames[i];
						map.put(subName, value);
					}
					value=buildSearchFilter(map);
				
				}
				
				//如果是子查询，则需要将里面的内容循环调用
			}else if(operator==QueryOperator.EXISTS||operator==QueryOperator.NEXISTS){
				searchName=names[1];
				Map<String,Object> map=(Map<String,Object>)value;
				// 子查询的时候 值是一个SearchFilter列表
				value=buildSearchFilter(map);
			}else{
				searchName=names[1];
			}
			SearchFilter filter = new SearchFilter(searchName, operator, value);
			
			
			
			filters.add(filter);
			
			
		}
		
		return filters;
	}
	
}
